﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Linq;

using SharpMUD.Framework.CorlibExtensions;
using SharpMUD.Framework.Services;
using SharpMUD.Framework.Services.Configuration;
using SharpMUD.Framework.ComponentModel;
using System.IO;
using SharpMUD.Framework.IO;
using System.Xml;

namespace SharpMUD.Application.BootStrap
{
    [Serializable]
    [XmlRoot("Service")]        [XmlInclude(typeof(InlineServiceConfigurationEntry))]
                                [XmlInclude(typeof(ExternalServiceConfigurationEntry))]
    public abstract class ServiceConfigurationEntryBase
    {
        public ServiceConfigurationEntryBase()
        { }

        public IService CreateServiceInstance()
        {
            return this.ServiceTypeType.Assembly.CreateInstance(this.ServiceTypeType.FullName, true) as IService;
        }

        public abstract T LoadConfiguration<T>()
            where T : IServiceConfiguration;
        public abstract void SaveConfiguration<T>(T configuration)
            where T : IServiceConfiguration;


        private string _ServiceType;
        
        [XmlAttribute("ServiceType")]
        public string ServiceType
        {
            get
            {
                return _ServiceType;
            }
            set
            {
                if (String.IsNullOrEmpty(value)) throw new InvalidOperationException();
                _cached_ServiceTypeType = null;
                _ServiceType = value;
            }
        }

        private Type _cached_ServiceTypeType;

        [XmlIgnore()]
        public Type ServiceTypeType
        {
            get
            {
                if (_cached_ServiceTypeType == null)
                    _cached_ServiceTypeType = AppDomain.CurrentDomain.GetAssemblyQualifiedType(_ServiceType);
                return _cached_ServiceTypeType;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (!IsValidServiceType(value)) throw new ArgumentException("The given type must implement the IService interface");
                _ServiceType = value.AssemblyQualifiedName;
                _cached_ServiceTypeType = value;
            }
        }

        private static bool IsValidServiceType(System.Type type)
        {
            return typeof(IService).IsAssignableFrom(type);
        }

        internal event EventHandler Dirtied;
    }

    [Serializable]
    public class InlineServiceConfigurationEntry
        : ServiceConfigurationEntryBase
    {
        private string _ConfigurationText;

        /*[XmlElement("config")]*/
        [XmlAnyElement()]
        public System.Xml.XmlNode[] ConfigurationTextXml
        {
            get
            {
                if(String.IsNullOrEmpty(this._ConfigurationText) )
                    return new System.Xml.XmlNode[] {};
                else
                {
                    var xmlDoc = new System.Xml.XmlDocument();
                    xmlDoc.LoadXml(this._ConfigurationText);
                    return xmlDoc.ChildNodes.Cast<XmlNode>().Where(xn=>!(xn.GetType()==typeof(XmlDeclaration))).ToArray();
                }
            }
            set
            {
                var sb = new StringBuilder();
                var xmlDoc = new System.Xml.XmlDocument();
                value.ToList().ForEach( n=> xmlDoc.AppendChild(xmlDoc.ImportNode(n,true)));
                var xmlWriter = new XmlTextWriter(new StringWriter(sb));
                xmlDoc.ChildNodes.Cast<XmlNode>().Where(xn => !(xn.GetType() == typeof(XmlDeclaration))).ToList().ForEach(xn => xn.WriteTo(xmlWriter));
                this._ConfigurationText = sb.ToString();
            }
        }
        [XmlIgnore]
        public string ConfigurationText
        {
            get
            {
                return _ConfigurationText;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _ConfigurationText = value;
            }
        }


        public override T  LoadConfiguration<T>()
        {
            return SharpMUD.Framework.IO.PersistanceProxy<T>.XmlSerializationString(this._ConfigurationText, null).Load();
        }

        public override void SaveConfiguration<T>(T configuration)
        {
            var sb = new StringBuilder();
            var tempPersistanceProxy = SharpMUD.Framework.IO.PersistanceProxy<T>.XmlSerializationString(this._ConfigurationText, new StringWriter(sb));
            tempPersistanceProxy.Save(configuration);
            this._ConfigurationText = sb.ToString();
        }
    }

    [Serializable]
    public class ExternalServiceConfigurationEntry
        : ServiceConfigurationEntryBase
    {
        private string _ConfigFile;

        [XmlAttribute("configFile")]
        public string ConfigFile
        {
            get
            {
                return _ConfigFile;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _ConfigFile = value;
            }
        }

        public override T LoadConfiguration<T>()
        {
            var tempPersistanceProxy = PersistanceProxy<T>.XmlSerializationFile(this._ConfigFile);
            return tempPersistanceProxy.Load();
        }

        public override void SaveConfiguration<T>(T configuration)
        {
            var tempPersistanceProxy = PersistanceProxy<T>.XmlSerializationFile(this._ConfigFile);
            tempPersistanceProxy.Save(configuration);
        }
    }
}